

#include "Serial_port.h"

//Constructor and destructor
Serial_port::Serial_port():serial_port_fd(0),nr_data(0) {
	; }
Serial_port::~Serial_port() {
	close(); }
//Setup a COM-port according to arguments
int Serial_port::open_port(char *device_file_path, int baud_rate = 57600,
		              int parity = NO_PARITY, int data_bits = 8,
					  int stop_bits = 1) {

	//Open serial port
//	serial_port_fd = open(device_file_path, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
	serial_port_fd = open(device_file_path, O_RDWR | O_NOCTTY );
//	serial_port_fd = open(device_file_path, O_RDWR | O_NOCTTY | O_NDELAY );
	//Check if succesfull
	if (serial_port_fd==-1){
		//Inform user of failure
		printf("Open device file failed");
		return 0;}

	//Set file descriptor flags to zero
//	fcntl(serial_port_fd, F_SETFL, FNDELAY); //return immedialtely
//	fcntl(serial_port_fd, F_SETFL, 0);
	//fcntl(serial_port_fd, F_SETFL, NDELAY);
	//Get current setting of port
	tcgetattr(serial_port_fd, &settings);

	set_baud_rate(baud_rate);
	set_parity(parity);
	set_data_bits(data_bits);
	set_stop_bits(stop_bits);
	set_time_out(1,0);
//	set_time_out(1,1);

	int hardware_flow_control = HARDWARE_FLOW_CONTROL_DISABLE;
//	int hardware_flow_control = HARDWARE_FLOW_CONTROL_ENABLE;
	set_hardware_flow_control(hardware_flow_control);

	//Set local mode
	settings.c_cflag |= CLOCAL;
	//Set the port to read incoming data
	settings.c_cflag |= CREAD;

	//Set raw input
	settings.c_lflag &= ~ICANON;
	//Disable SIGINTR, SIGSUSUP, SIGDSUSP, and SIGQUIT signals
	settings.c_lflag &= ~ISIG;
	//Map uppercase to lower case
//	settings.c_cflag |= XCASE;
	//Disable echoing of input
	settings.c_lflag &= ~ECHO;
	//Disable echoing of erase charachters
	settings.c_lflag &= ~ECHOE;

	//Disable outgoing software flow control
	settings.c_iflag &= ~IXON;
	//Disable incoming software flow control
	settings.c_iflag &= ~IXOFF;
	//Disable flow start control by any character
	settings.c_iflag &= ~IXANY;
	//Don't map NL to CR
	settings.c_iflag &= ~INLCR;
	//Don't map CR to NL
	settings.c_iflag &= ~ICRNL;
	//Don't map uppercase to lower case
	settings.c_iflag &= ~IUCLC;

	//Set raw output (no processing of output)
	settings.c_oflag &= ~OPOST;

	set_settings();

	return 1; }

//Close COM-port
int Serial_port::close() {
	//Check if port is open
	if (serial_port_fd == -1) {
		printf("Close attempt failed, Serial port not open");
		return  0; }

	//Close port
	::close(serial_port_fd);
	return 1; }

//Read line from serial port
int Serial_port::read_line(unsigned char *read_buffer, int buffer_size) {
	unsigned char byte_read = '\0';
	int  nr_bytes_read = 0;
	int  read_error = 0;
	//Read one row in serial buffer
	do {
		//Check for buffer overflow
		if(nr_bytes_read>(buffer_size-2)) {
			printf("Read buffer overflow. nr_bytes_read = %i, buffer_size = %i\n",nr_bytes_read,buffer_size);
			read_buffer[nr_bytes_read]='\0';
			printf("%s\n",read_buffer);
			printf("Buffer: %i %i %i \n", read_buffer[0], read_buffer[1], read_buffer[2]);
			read_error=1;
			break; }
		//Read 1 byte
		if(::read(serial_port_fd, &byte_read, 1)==-1) {
			printf("Read operation failed\n");
			read_error=1;
			break; }
		//Add the byte read to the buffer
		read_buffer[nr_bytes_read] = byte_read;
		nr_bytes_read++;
	} while (byte_read != '\n');

	//Add string null-sign
	read_buffer[nr_bytes_read]='\0';
	if (read_error==1) {
		return -nr_bytes_read; }
	return nr_bytes_read; }

//Read "bytes" byte from serial port
int Serial_port::read_binary(unsigned char *read_buffer, int bytes) {
	//Read "bytes" byte
	//If there is data int he buffer take it first
//	if(nr_data>0){
//		if (nr_data>bytes){
//			//Take out desired nr of bytes
//			for(int i=0;i<bytes;i++){
//				read_buffer[i]=buffer[i];}
//			//Move the remaining nr of bytes
//			for(int i=bytes;i<nr_data;i++){
//				buffer[i-bytes]=buffer[i];}
//			//Read in new bytes if available
//			nr_data=nr_data-bytes+read(serial_port_fd, &buffer[nr_data-bytes],512);
//			printf("%i\n",nr_data);
//			return 1;}
//		else {
//			for(int i=0;i<nr_data;i++){
//			read_buffer[i]=buffer[i];}
//		}
//	}
	//Read data
//	int bytes_read=nr_data;
	int bytes_read=0;
	while (bytes_read<bytes) {
//		printf("reading one byte\n");
		bytes_read=bytes_read+read(serial_port_fd, &read_buffer[bytes_read], bytes-bytes_read);
	}
	//Read remaining data to buffer
//	nr_data=read(serial_port_fd, buffer,512);
//	printf("%i\n",nr_data);

	return 1; }

int Serial_port::read_nonblocking(unsigned char *read_buffer, int bytes) {
	return read(serial_port_fd, read_buffer, bytes);}

//Write data of given length to serial port
int Serial_port::write_binary(unsigned char *write_buffer, int data_size) {
	int nr_bytes_written = 0;
	nr_bytes_written=write(serial_port_fd, write_buffer, data_size);
	if(nr_bytes_written<data_size) {
//		printf("%i %i \n",nr_bytes_written,data_size);
//		printf("%i\n",serial_port_fd);
		printf("Write operation failed\n");
		return 0; }
	return 1; }

//Clear read and write buffers
int Serial_port::flush() {
	if(tcflush(serial_port_fd,TCIOFLUSH)==-1) {
		printf("Flush operation failed\n"); }
	nr_data=0;
	return 1; }

int Serial_port::set_baud_rate(int baud_rate){
	//Set baud_rate
	switch (baud_rate) {
		case 115200:
			cfsetispeed(&settings, B115200);
			cfsetospeed(&settings, B115200);
			break;
		//case 76800:
			//cfsetispeed(&settings, B76800);
			//cfsetospeed(&settings, B76800);
			//break;
		case 57600:
			cfsetispeed(&settings, B57600);
			cfsetospeed(&settings, B57600);
			break;
		case 38400:
			cfsetispeed(&settings, B38400);
			cfsetospeed(&settings, B38400);
			break;
		case 19200:
			cfsetispeed(&settings, B19200);
			cfsetospeed(&settings, B19200);
			break;
		case 9600:
			cfsetispeed(&settings, B9600);
			cfsetospeed(&settings, B9600);
			break;
		case 4800:
			cfsetispeed(&settings, B4800);
			cfsetospeed(&settings, B4800);
			break;
		default:
			printf("%i not a valid baud rate\n",baud_rate);
			return 0; }
	set_settings();
	return 1;}

int Serial_port::set_parity(int parity){
	//Set parity
	switch (parity) {
		case EVEN_PARITY:
			settings.c_cflag |= PARENB;
			settings.c_cflag &= ~PARODD;
			settings.c_iflag |= INPCK;
			settings.c_iflag |= ISTRIP;
			break;
		case ODD_PARITY:
			settings.c_cflag |= PARENB;
			settings.c_cflag |= PARODD;
			settings.c_iflag |=INPCK;
			settings.c_iflag |= ISTRIP;
			break;
		case NO_PARITY:
			settings.c_cflag &= ~PARENB;
			break;
		case SPACE_PARITY:
			//Gives space parity together with 8 bit character size (7S1-2)
			settings.c_cflag &= ~PARENB;
			settings.c_iflag |=INPCK;
			settings.c_iflag |= ISTRIP;
		default:
			printf("%i not a valid parity value\n", parity);
			return 0; }
	set_settings();
	return 1;}

int Serial_port::set_data_bits(int data_bits){
	//Set character size (number of data bits)
	switch (data_bits) {
		case 8:
			settings.c_cflag &= ~CSIZE;  //??? Hade kommaterat ut den men vet inte varför?
			settings.c_cflag |= CS8;
			break;
		case 7:
			settings.c_cflag &= ~CSIZE;
			settings.c_cflag |= CS7;
			break;
		case 6:
			settings.c_cflag &= ~CSIZE;
			settings.c_cflag |= CS6;
			break;
		case 5:
			settings.c_cflag &= ~CSIZE;
			settings.c_cflag |= CS5;
			break;
		default:
			printf("%i not a valid number of data bits\n", data_bits);
			return 0; }
	set_settings();
	return 1;}

int Serial_port::set_stop_bits(int stop_bits){
	//Set stop bit (number of stop bits)
	switch (stop_bits) {
		case 1:
			settings.c_cflag &= ~CSTOPB;
			break;
		case 2:
			settings.c_cflag |= CSTOPB;
			break;
		default:
			printf("%i is not a valid number of stop bits\n", stop_bits);
			return 0; }
	set_settings();
	return 1;}

int Serial_port::set_time_out(int vmin,double vtime){
	//Set timout settings
	settings.c_cc[VMIN] = vmin;  //no min nr of bytes
	settings.c_cc[VTIME] = vtime;//1; //0.1s
	set_settings();
	return 1;}

int Serial_port::set_hardware_flow_control(int hardware_flow_control){
	switch (hardware_flow_control) {
		case HARDWARE_FLOW_CONTROL_DISABLE:
			//settings.c_cflag &= ~CNEW_RTSCTS;
			settings.c_cflag &= ~CRTSCTS;
			break;
		case HARDWARE_FLOW_CONTROL_ENABLE:
			//settings.c_cflag |= CNEW_RTSCTS;
			settings.c_cflag |= CRTSCTS;
			break;
		default:
			printf("%i not a valid hardware control option\n", hardware_flow_control);
			return 0; }
	set_settings();
	return 1;}

int Serial_port::set_dtr_pin(int dtr){
	int status;
	ioctl(serial_port_fd, TIOCMGET, &status);
	switch (dtr) {
		case 1:
			status |= TIOCM_DTR;
			break;
		case 0:
			status &= ~TIOCM_DTR;
			break;
		default:
			printf("%i not a valid DTR value\n", dtr);
			return 0; }
	ioctl(serial_port_fd, TIOCMSET, &status);
	return 1;}


int Serial_port::set_rts_pin(int rts){
	//Note that this setting might be overridden by hardware flow control
	int status;
	ioctl(serial_port_fd, TIOCMGET, &status);
	switch (rts) {
		case 1:
			status |= TIOCM_RTS;
			break;
		case 0:
			status &= ~TIOCM_RTS;
			break;
		default:
			printf("%i not a valid RTS value\n", rts);
			return 0; }
	ioctl(serial_port_fd, TIOCMSET, &status);
	return 1;}

int Serial_port::set_settings(void){
	//Write changes to port
	tcsetattr(serial_port_fd, TCSANOW, &settings);
	return 1;}
